// Io object in python

#include <python2.4/Python.h>
#include "IoPython.h"
#include "IoState.h"
#include "PyIo.h"
#include "IoBlock.h"

typedef struct {
    PyObject_HEAD
    /* Type-specific fields go here. */
	IoObject *ioobject;
} pyIoObject;

static PyMethodDef pyIo_methods[] = {
    {NULL, NULL, 0, NULL}           /* sentinel */
};

static PyObject *
pyIo_getattr(pyIoObject *obj, char *name)
{
	// Look through the slots for the correctly named one, then convert it.
	// If I here know it's a method, and can't tell in convert, do the PyIoMethod wrapping here.
	// TODO: Memory management here! Esp. with all the strings/symbols we're making.
	IoObject *self = obj->ioobject;
	IoSymbol *ioname = IOSYMBOL(name);
	IoObject *slot = IoObject_getSlot_(self, ioname);
	if(slot) {
		if(ISMESSAGE(slot)) {
			printf("Slot is a message\n");
		} else if(ISBLOCK(slot)) {
			return newPyIoMethod(self, ioname);
		} else if(ISIOPYTHON(slot)) {
			return DATA(slot)->data;
		} else if(ISSEQ(slot)) {
			return convertIo(self, slot);
		} else if(ISNIL(slot)){
			IoObject *parent = IoObject_firstProto(self);
			if(parent) {
				if(ISIOPYTHON(parent)) {
					return newPyIoMethod(parent, ioname);
				}
			}
			IoObject *slot = IoObject_getSlot_(parent, ioname);
			if(!ISNIL(slot)) {
				if(ISBLOCK(slot)) {
					return newPyIoMethod(self, ioname);
				} else {
					return convertIo(self, slot);
				}
			}
			// Could mean parent is a PyIo and we're a subclass
			Py_INCREF(Py_None);
			return Py_None;
		} else {
			// Slot is unknown. Passing to convert
		}
		
		return convertIo(self, slot);
	} else {
		printf(" !!! Error Did not get a slot !!!\n");
	}
    return Py_FindMethod(pyIo_methods, (PyObject *)obj, name);
}

static PyObject *
pyIo_call(pyIoObject *obj, PyObject *args, PyObject *other)
{
	printf("Returning same object - call not really implemented yet.");
	IoObject *o = obj->ioobject;
	if(ISBLOCK(o)) {
		// We're callable
		
	}
	Py_INCREF(obj);
    return (PyObject *) obj;
}

static PyTypeObject pyIoType = {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "IoPython.pyIo",             /*tp_name*/
    sizeof(pyIoObject), /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    0,                         /*tp_dealloc*/
    0,                         /*tp_print*/
    (getattrfunc) pyIo_getattr,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    0,                         /*tp_compare*/
    0,                         /*tp_repr*/
    0,                         /*tp_as_number*/
    0,                         /*tp_as_sequence*/
    0,                         /*tp_as_mapping*/
    0,                         /*tp_hash */
    pyIo_call,                         /*tp_call*/
    0,                         /*tp_str*/
    0,                         /*tp_getattro*/
    0,                         /*tp_setattro*/
    0,                         /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT,        /*tp_flags*/
    "pyIo objects",           /* tp_doc */
};

PyMODINIT_FUNC
initpyIo(void) 
{
    PyObject* m;
	PyObject *meth;
	
    pyIoType.tp_new = PyType_GenericNew;
	
    if (PyType_Ready(&pyIoType) < 0)
        return;

	meth = initPyIoMethod();
	
	if(PyType_Ready(meth) < 0)
		return;
		

    m = Py_InitModule3("IoPython", pyIo_methods,
                       "Module with wrappers around Io objects.");

    Py_INCREF(&pyIoType);
	Py_INCREF(meth);
    PyModule_AddObject(m, "pyIo", (PyObject *)&pyIoType);
	PyModule_AddObject(m, "pyIoMethod", meth);
}

PyObject *newPyIo(IoObject *obj) {
	pyIoObject *o = PyObject_New(pyIoObject, &pyIoType);
	o->ioobject = obj;
	return (PyObject *) o;
}

